﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace 通用过站与数采.BLL
{
    public class KeyboardHook
    {
        public delegate void KeyboardDeletegate(InputCodes input);
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;
        private const int VK_SHIFT = 0x10;
        public struct InputCodes
        {
            public int VirtKey;

            public int ScanCode;

            public string KeyName;

            public uint Ascll;

            public char Chr;

            public string OriginalChrs;

            public string OriginalAsciis;

            public string OriginalBarCode;

            public string InputStr;

            public bool IsValid;

            public DateTime Time;

            public int err;
        }

        private struct EventMsg
        {
            public int message;

            public int paramL;

            public int paramH;

            public int Time;

            public int hwnd;
        }

        private delegate int HookProc(int nCode, int wParam, IntPtr lParam);

        private static HookProc hookproc;

        private InputCodes intputStr;

        private int hKeyboardHook = 0;

        private StringBuilder sbBarCode = new StringBuilder();

        public event KeyboardDeletegate BarCodeEvent;


        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        [DllImport("user32", EntryPoint = "GetKeyNameText")]
        private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize);

        [DllImport("user32", EntryPoint = "GetKeyboardState")]
        private static extern int GetKeyboardState(byte[] pbKeyState);

        [DllImport("user32", EntryPoint = "ToAscii")]
        private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            lock (this)
            {
                bool flag = false;
                if (nCode == 0)
                {
                    EventMsg eventMsg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg));
                    if (wParam == WM_KEYUP)
                    {
                        intputStr.VirtKey = (eventMsg.message & 0xFF);
                        intputStr.ScanCode = (eventMsg.paramL & 0xFF);
                        StringBuilder stringBuilder = new StringBuilder(225);
                        if (GetKeyNameText(intputStr.ScanCode * 65536, stringBuilder, 255) > 0)
                        {
                            intputStr.KeyName = stringBuilder.ToString().Trim('\0');
                        }
                        else
                        {
                            intputStr.KeyName = "";
                        }
                        byte[] array = new byte[256];
                        uint lpChar = 0u;
                        GetKeyboardState(array);
                        if (ToAscii(intputStr.VirtKey, intputStr.ScanCode, array, ref lpChar, 0))
                        {
                            intputStr.Ascll = lpChar;
                            intputStr.Chr = Convert.ToChar(lpChar);
                        }
                        else
                        {
                            flag = true;
                        }
                        if (DateTime.Now.Subtract(intputStr.Time).TotalMilliseconds > 100)
                        {
                            sbBarCode.Remove(0, sbBarCode.Length);
                            sbBarCode.Append(intputStr.Chr.ToString());
                            intputStr.OriginalChrs = " " + Convert.ToString(intputStr.Chr);
                            intputStr.OriginalAsciis = " " + Convert.ToString(intputStr.Ascll);
                            intputStr.OriginalBarCode = Convert.ToString(intputStr.Chr);
                        }
                        else if (!flag)
                        {
                            sbBarCode.Append(intputStr.Chr.ToString());
                            if ((eventMsg.message & 0xFF) == 13)
                            {
                                intputStr.InputStr = sbBarCode.ToString();
                                intputStr.IsValid = true;
                                sbBarCode.Remove(0, sbBarCode.Length);
                            }
                        }
                        try
                        {
                            if (this.BarCodeEvent != null && intputStr.IsValid)
                            {
                                AsyncCallback callback = AsyncBack;
                                Delegate[] invocationList = this.BarCodeEvent.GetInvocationList();
                                Delegate[] array2 = invocationList;
                                for (int i = 0; i < array2.Length; i++)
                                {
                                    KeyboardDeletegate bardCodeDeletegate = (KeyboardDeletegate)array2[i];
                                    try
                                    {
                                        bardCodeDeletegate.BeginInvoke(intputStr, callback, bardCodeDeletegate);
                                    }
                                    catch (Exception ex)
                                    {
                                        throw ex;
                                    }
                                }
                                intputStr.InputStr = "";
                                intputStr.OriginalChrs = "";
                                intputStr.OriginalAsciis = "";
                                intputStr.OriginalBarCode = "";
                                intputStr.err = 0;
                            }
                        }
                        catch (Exception)
                        {
                            throw;
                        }
                        finally
                        {
                            intputStr.IsValid = false;
                            intputStr.Time = DateTime.Now;
                        }
                    }
                }
                return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
            }
        }

        public void AsyncBack(IAsyncResult ar)
        {
            KeyboardDeletegate barCodeDeletegate = ar.AsyncState as KeyboardDeletegate;
            barCodeDeletegate.EndInvoke(ar);
        }

        public bool Start()
        {
            if (hKeyboardHook == 0)
            {
                hookproc = new HookProc(KeyboardHookProc);
                IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
                hKeyboardHook = SetWindowsHookEx(13, hookproc, moduleHandle, 0);

            }
            return hKeyboardHook != 0;
        }

        public bool Stop()
        {
            if (hKeyboardHook != 0)
            {
                return UnhookWindowsHookEx(hKeyboardHook);
            }
            return true;
        }
    }
}
